package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.CommissionItemDTO;
import info.batcloud.fanli.core.dto.CommissionItemFetchDTO;
import info.batcloud.fanli.core.dto.UserItemSelectionDTO;
import info.batcloud.fanli.core.entity.CommissionItem;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.entity.UserItemSelection;
import info.batcloud.fanli.core.enums.Authorize;
import info.batcloud.fanli.core.helper.TimeInfoHelper;
import info.batcloud.fanli.core.repository.CommissionItemRepository;
import info.batcloud.fanli.core.repository.UserItemSelectionRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.CommissionItemService;
import info.batcloud.fanli.core.service.UserAuthorizeService;
import info.batcloud.fanli.core.service.UserItemSelectionService;
import info.batcloud.fanli.core.service.UserService;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserItemSelectionServiceImpl implements UserItemSelectionService {

    @Inject
    private UserItemSelectionRepository userItemSelectionRepository;

    @Inject
    private CommissionItemRepository commissionItemRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private UserService userService;

    @Inject
    private UserAuthorizeService userAuthorizeService;

    @Inject
    private CommissionItemService commissionItemService;

    @Override
    public long createUserItemSelection(ItemSelectionCreateParam param) {
        if (param.getItemIdList().size() == 0 && param.getSourceItemIdList().size() == 0) {
            return 0;
        }
        if(param.getSourceItemIdList().size() > 0) {
            List<CommissionItemFetchDTO> list = new ArrayList<>();
            for (int i = 0; i < param.getSourceItemIdList().size(); i++) {
                list.add(commissionItemService.fetchOnlineBySourceItemIdAndEcomPlat(param.getSourceItemIdList().get(i),
                        param.getEcomPlatList().get(i)));
            }
            List<CommissionItemDTO> commissionItemDTOList = commissionItemService.saveCommissionItemListSync(list);
            param.getItemIdList().addAll(commissionItemDTOList.stream().map(o -> o.getId()).collect(Collectors.toList()));

        }
        UserItemSelection uis = new UserItemSelection();
        uis.setContent(param.getContent());
        uis.setCreateTime(new Date());
        List ids = param.getItemIdList().stream().map(o -> o + "").collect(Collectors.toList());
        uis.setItemIds(String.join(",", ids.size() <= 9 ? ids : ids.subList(0, 9)));
        uis.setShareTimes(0);
        uis.setUserId(param.getUserId());
        if (userAuthorizeService.checkAuthorize(param.getUserId(), Authorize.ITEM_SELECTION_MANAGER)) {
            uis.setRecommend(true);
            uis.setFitShareTimes(RandomUtils.nextInt(2000, 5000));
        }
        userItemSelectionRepository.save(uis);
        return uis.getId();
    }

    @Override
    public void addShareTimes(long itemSelectionId, int times) {
        UserItemSelection uis = userItemSelectionRepository.findOne(itemSelectionId);
        uis.setShareTimes(uis.getShareTimes() + times);
        userItemSelectionRepository.save(uis);
    }

    @Override
    @Transactional
    public void deleteById(long userId, long id) {
        userItemSelectionRepository.deleteByUserIdAndId(userId, id);
    }

    @Override
    public Paging<UserItemSelectionDTO> search(SearchParam param) {
        Specification<UserItemSelection> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getRecommend() != null) {
                expressions.add(cb.equal(root.get("recommend"), param.getRecommend()));
            }
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("userId"), param.getUserId()));
            }
            expressions.add(cb.equal(root.get("deleted"), false));
            return predicate;
        };
        Sort sort;
        if (param.getSort() != null) {
            switch (param.getSort()) {
                case SHARE_TIMES_DESC:
                    sort = new Sort(Sort.Direction.DESC, "shareTimes");
                    break;
                case ID_DESC:
                    sort = new Sort(Sort.Direction.DESC, "id");
                    break;
                default:
                    sort = new Sort(Sort.Direction.DESC, "id");
            }
        } else {
            sort = new Sort(Sort.Direction.DESC, "id");
        }
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<UserItemSelection> page = userItemSelectionRepository.findAll(specification, pageable);
        List<UserItemSelectionDTO> dtoList = toDtoList(page.getContent());
        return Paging.of(dtoList, Long.valueOf(page.getTotalElements()).intValue(),
                param.getPage(), param.getPageSize());
    }

    private List<UserItemSelectionDTO> toDtoList(List<UserItemSelection> selections) {

        List<Long> itemIds = new ArrayList<>();
        List<Long> userIds = new ArrayList<>();
        List<UserItemSelectionDTO> dtoList = new ArrayList<>();
        selections.forEach(item -> {
            itemIds.addAll(item.getItemIdList());
            userIds.add(item.getUserId());
        });
        List<CommissionItem> commissionItems = commissionItemRepository.findByIdIn(itemIds);
        Map<Long, CommissionItem> commissionItemMap = new HashMap<>();
        for (CommissionItem commissionItem : commissionItems) {
            commissionItemMap.put(commissionItem.getId(), commissionItem);
        }
        List<User> userList = userRepository.findByIdInOrderByIdAsc(userIds);
        Map<Long, User> userMap = new HashMap<>();
        for (User user : userList) {
            userMap.put(user.getId(), user);
        }
        selections.forEach(selection -> {
            UserItemSelectionDTO dto = new UserItemSelectionDTO();
            dto.setId(selection.getId());
            dto.setContent(selection.getContent());
            dto.setCreateTime(selection.getCreateTime());
            dto.setTimeInfo(TimeInfoHelper.shortTimeInfo(selection.getCreateTime()));
            User user = userMap.get(selection.getUserId());
            dto.setUserId(user.getId());
            dto.setUserAvatarUrl(userService.determineUserAvatar(user.getAvatarUrl()));
            dto.setUserNickname(userService.determineUserNickname(user.getNickname()));
            dto.setItemList(new ArrayList<>());
            int fitTimes = selection.getFitShareTimes();
            if(fitTimes == 0) {
                fitTimes = RandomUtils.nextInt(3000, 5000);
            }
            if (selection.getShareTimes() < fitTimes) {
                long millis = System.currentTimeMillis() - selection.getCreateTime().getTime();
                Long times = fitTimes * millis / (60 * 60 * 1000);
                dto.setShareTimes(times.intValue() > fitTimes ? fitTimes : times.intValue());
            } else {
                dto.setShareTimes(selection.getShareTimes());
            }
            for (Long itemId : selection.getItemIdList()) {
                CommissionItem eItem = commissionItemMap.get(itemId);
                if (eItem != null) {
                    UserItemSelectionDTO.Item itemDto = new UserItemSelectionDTO.Item();
                    itemDto.setId(eItem.getId());
                    itemDto.setOriginPrice(eItem.getOriginPrice());
                    itemDto.setPrice(eItem.getPrice());
                    itemDto.setPicUrl(eItem.getPicUrl());
                    dto.getItemList().add(itemDto);
                }
            }
            dtoList.add(dto);
        });
        return dtoList;
    }

}
